<template>
  <div class="draggable" :style="style">
    <div class="drag-hander" @mousedown="startDragging">
      <slot name="head"></slot>
    </div>
    <div
      v-for="(item, key) in handlers"
      :key="key"
      class="resizable"
      :class="[item]"
      @mousedown="startResizing(item)"
    ></div>
    <slot></slot>
  </div>
</template>

<script lang="ts" setup>
  import { computed, CSSProperties, reactive, watch } from 'vue';
  import { DomRect, Position } from './types';

  const props = withDefaults(
    defineProps<{
      width: number;
      height: number;
      left: number;
      top: number;
      minW?: number;
      minH?: number;
    }>(),
    {
      width: 800,
      height: 600,
      minW: 600,
      minH: 400,
      left: 0,
      top: 0,
    }
  );

  type Direction =
    | 'right'
    | 'bottom'
    | 'left'
    | 'top'
    | 'tr'
    | 'br'
    | 'bl'
    | 'tl';
  const position: Position = reactive({ x: 0, y: 0 });
  const handlers: Direction[] = [
    'right',
    'bottom',
    'left',
    'top',
    'tr',
    'br',
    'bl',
    'tl',
  ];
  const domRect: DomRect = reactive({
    height: 600,
    width: 800,
  });
  let lastMouseX = 0;
  let lastMouseY = 0;
  const emit = defineEmits(['draggingStarted', 'draggingEnd']);

  const startDragging = () => {
    const de = window.event as MouseEvent;
    lastMouseX = de.clientX;
    lastMouseY = de.clientY;

    document.onmousemove = (me: MouseEvent) => {
      const deltaX = me.clientX - lastMouseX;
      const deltaY = me.clientY - lastMouseY;
      lastMouseX = me.clientX;
      lastMouseY = me.clientY;

      position.x += deltaX;
      position.y += deltaY;
    };
    document.onmouseup = () => {
      if (position.x + 140 >= window.innerWidth) {
        position.x = window.innerWidth - 120;
      }

      if (position.y + 46 >= window.innerHeight) {
        position.y = window.innerHeight - 46;
      }
      if (position.y < 0) {
        position.y = 0;
      }
      if (position.x < -domRect.width - 120) {
        position.x = 0;
      }

      document.onmousemove = null;
    };
  };

  const draggingEnd = () => {
    document.onmousemove = null;
    emit('draggingEnd');
  };
  /* 各个方向的处理函数 */
  const directionMethods = {
    right() {
      const de = window.event as MouseEvent;
      lastMouseX = de.clientX;

      document.onmousemove = (me: MouseEvent) => {
        const deltaX = me.clientX - lastMouseX;
        domRect.width += deltaX;
        lastMouseX = me.clientX;

        if (deltaX < 0) {
          if (props.minW > domRect.width) {
            domRect.width = props.minW;
          }
        }
      };

      document.onmouseup = draggingEnd;
    },
    bottom() {
      const de = window.event as MouseEvent;
      lastMouseY = de.clientY;

      document.onmousemove = (me: MouseEvent) => {
        const deltaY = me.clientY - lastMouseY;
        domRect.height += deltaY;
        lastMouseY = me.clientY;

        if (deltaY < 0) {
          if (props.minH > domRect.height) {
            domRect.height = props.minH;
          }
        }
      };
      document.onmouseup = draggingEnd;
    },
    left() {
      const de = window.event as MouseEvent;
      lastMouseX = de.clientX;
      /* 鼠标点击时候的坐标 */
      document.onmousemove = (me: MouseEvent) => {
        const deltaX = me.clientX - lastMouseX;
        lastMouseX = me.clientX;

        /* 判断往右拖拽 */
        if (deltaX > 0) {
          if (domRect.width > props.minW) {
            position.x += deltaX;
            domRect.width -= deltaX;
          }
        } else if (deltaX < 0) {
          position.x += deltaX;
          domRect.width -= deltaX;
        }
      };
      document.onmouseup = draggingEnd;
    },
    top() {
      const de = window.event as MouseEvent;
      lastMouseY = de.clientY;

      document.onmousemove = (me: MouseEvent) => {
        const deltaY = me.clientY - lastMouseY;
        lastMouseY = me.clientY;

        /* 判断往下拖拽 */
        if (deltaY > 0) {
          if (domRect.height > props.minH) {
            position.y += deltaY;
            domRect.height -= deltaY;
          }
        } else if (deltaY < 0) {
          position.y += deltaY;
          domRect.height -= deltaY;
        }
      };

      document.onmouseup = draggingEnd;
    },
    /* 四个角 */
    tr() {
      const de = window.event as MouseEvent;
      lastMouseY = de.clientY;
      lastMouseX = de.clientX;

      document.onmousemove = (me: MouseEvent) => {
        const deltaY = me.clientY - lastMouseY;
        const deltaX = me.clientX - lastMouseX;

        lastMouseY = me.clientY;
        lastMouseX = me.clientX;
        if (deltaX > 0) {
          domRect.width += deltaX;
        }

        if (deltaX < 0) {
          domRect.width += deltaX;
          if (props.minW > domRect.width) {
            domRect.width = props.minW;
          }
        }
        if (deltaY > 0) {
          if (domRect.height > props.minH) {
            position.y += deltaY;
            domRect.height -= deltaY;
          }
        } else if (deltaY < 0) {
          position.y += deltaY;
          domRect.height -= deltaY;
        }
      };
      document.onmouseup = draggingEnd;
    },
    br() {
      const de = window.event as MouseEvent;
      lastMouseY = de.clientY;
      lastMouseX = de.clientX;
      document.onmousemove = (me: MouseEvent) => {
        const deltaY = me.clientY - lastMouseY;
        const deltaX = me.clientX - lastMouseX;
        domRect.height += deltaY;
        domRect.width += deltaX;
        lastMouseY = me.clientY;
        lastMouseX = me.clientX;

        if (deltaX < 0) {
          if (props.minW > domRect.width) {
            domRect.width = props.minW;
          }
        }
        if (deltaY < 0) {
          if (props.minH > domRect.height) {
            domRect.height = props.minH;
          }
        }
      };
      document.onmouseup = draggingEnd;
    },
    bl() {
      const de = window.event as MouseEvent;
      lastMouseY = de.clientY;
      lastMouseX = de.clientX;
      document.onmousemove = (me: MouseEvent) => {
        const deltaY = me.clientY - lastMouseY;
        const deltaX = me.clientX - lastMouseX;
        domRect.height += deltaY;
        if (deltaX > 0) {
          if (domRect.width > props.minW) {
            position.x += deltaX;
            domRect.width -= deltaX;
          }
        } else if (deltaX < 0) {
          position.x += deltaX;
          domRect.width -= deltaX;
        }
        if (deltaY < 0) {
          if (props.minH > domRect.height) {
            domRect.height = props.minH;
          }
        }

        lastMouseY = me.clientY;
        lastMouseX = me.clientX;
      };
      document.onmouseup = draggingEnd;
    },
    tl() {
      const de = window.event as MouseEvent;
      lastMouseY = de.clientY;
      lastMouseX = de.clientX;
      document.onmousemove = (me: MouseEvent) => {
        const deltaY = me.clientY - lastMouseY;
        const deltaX = me.clientX - lastMouseX;

        if (deltaX > 0) {
          if (domRect.width > props.minW) {
            position.x += deltaX;
            domRect.width -= deltaX;
          }
        } else if (deltaX < 0) {
          position.x += deltaX;
          domRect.width -= deltaX;
        }
        if (deltaY > 0) {
          if (domRect.height > props.minH) {
            position.y += deltaY;
            domRect.height -= deltaY;
          }
        } else if (deltaY < 0) {
          position.y += deltaY;
          domRect.height -= deltaY;
        }

        lastMouseY = me.clientY;
        lastMouseX = me.clientX;
      };
      document.onmouseup = draggingEnd;
    },
  };
  /* 开始拖拽调整大小 */
  const startResizing = (direction: Direction) => {
    if (directionMethods[direction]) {
      emit('draggingStarted');
      directionMethods[direction]();
    }
  };
  const style = computed((): CSSProperties => {
    return {
      top: `${position.y}px`,
      left: `${position.x}px`,
      width: `${domRect.width}px`,
      height: `${domRect.height}px`,
    };
  });

  watch(
    props,
    () => {
      domRect.height = props.height;
      domRect.width = props.width;

      position.x = props.left;
      position.y = props.top;
    },
    {
      deep: true,
      immediate: true,
    }
  );
</script>

<style scoped lang="scss">
  @import './style';
</style>
